package com.infobip.ftpclient;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.StringTokenizer;

public class FTP {

	private Socket socket = null;

	private BufferedReader bufferedReader = null;

	private BufferedWriter bufferedWriter = null;

	public FTP() {

	}

	/**
	 * Connects to FTP server with given username and password.
	 * 
	 * @throws IOException
	 */
	public void connect(String host, int port, String user, String pass)
			throws IOException {
		if (socket != null) {
			throw new IOException("FTP is already connected. Disconnect first.");
		}
		try {
			socket = new Socket(host, port);
		} catch (UnknownHostException e) {
			throw e;
		}

		bufferedReader = new BufferedReader(new InputStreamReader(
				socket.getInputStream()));
		bufferedWriter = new BufferedWriter(new OutputStreamWriter(
				socket.getOutputStream()));

		String response = readResponse();

		if (!response.startsWith("220")) {
			throw new IOException(
					"Expected response: 220, but response from FTP server is: "
							+ response.substring(0, 3));
		}

		sendCommand("USER " + user);

		response = readResponse();

		if (!response.startsWith("331")) {
			throw new IOException(
					"Received wrong response after sending the username.");
		}

		sendCommand("PASS " + pass);

		response = readResponse();

		if (!response.startsWith("230")) {
			throw new IOException("Unable to log in with the given password");
		}
	}

	/**
	 * Disconnects from FTP server.
	 */
	public void disconnect() throws IOException {
		try {
			sendCommand("QUIT");
		} finally {
			socket = null;
		}
	}

	/**
	 * Sends a file FTP server. Returns true if the file transfer was
	 * successful. The file is sent in passive mode.
	 */
	public synchronized boolean stor(InputStream inputStream, String filename)
			throws IOException {

		BufferedInputStream input = new BufferedInputStream(inputStream);

		sendCommand("PASV");
		String response = readResponse();

		if (!response.startsWith("227")) {
			throw new IOException("Could not request passive mode");
		}

		String ip = null;
		int port = -1;
		int opening = response.indexOf('(');
		int closing = response.indexOf(')', opening + 1);
		if (closing > 0) {
			String dataLink = response.substring(opening + 1, closing);
			StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
			try {
				ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "."
						+ tokenizer.nextToken() + "." + tokenizer.nextToken();
				port = Integer.parseInt(tokenizer.nextToken()) * 256
						+ Integer.parseInt(tokenizer.nextToken());
			} catch (Exception e) {
				throw new IOException("Received bad data link information");
			}
		}

		sendCommand("STOR " + filename);

		Socket dataSocket = new Socket(ip, port);

		response = readResponse();

		if (!response.startsWith("150")) {
			throw new IOException("Not allowed to send the file");
		}

		BufferedOutputStream output = new BufferedOutputStream(
				dataSocket.getOutputStream());
		byte[] buffer = new byte[4096];
		int bytesRead = 0;
		while ((bytesRead = input.read(buffer)) != -1) {
			output.write(buffer, 0, bytesRead);
		}
		output.flush();
		output.close();
		input.close();

		response = readResponse();

		return response.startsWith("226");
	}

	/**
	 * Sends a command to the FTP server.
	 * 
	 * @param command
	 * @throws IOException
	 */
	private void sendCommand(String command) throws IOException {
		if (socket == null) {
			throw new IOException("SimpleFTP is not connected.");
		}
		try {
			bufferedWriter.write(command + "\r\n");
			bufferedWriter.flush();
		} catch (IOException e) {
			socket = null;
			throw e;
		}
	}

	/**
	 * Read response from FTP Server
	 * 
	 * @throws IOException
	 */
	private String readResponse() throws IOException {
		String line = "";
		String response = "";
		while (true) {
			line = bufferedReader.readLine();
			response = response + line + "\n";
			if (line.substring(3, 4).equals(" ")) {
				break;
			}
		}
		return response;
	}

}
